using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace Barbar.MapReport.DataSource.Esri {
  /// <summary>
  /// Shp File Header Block
  /// </summary>
  public class ShpHeader {
    #region Fields
    [ShpType(ShpByteOrder.BigEndian, ShpAtomicType.Integer)]
    private int m_FileCode;
    [ShpType(ShpByteOrder.BigEndian, ShpAtomicType.Integer)]
    private int m_Unused4;
    [ShpType(ShpByteOrder.BigEndian, ShpAtomicType.Integer)]
    private int m_Unused8;
    [ShpType(ShpByteOrder.BigEndian, ShpAtomicType.Integer)]
    private int m_Unused12;
    [ShpType(ShpByteOrder.BigEndian, ShpAtomicType.Integer)]
    private int m_Unused16;
    [ShpType(ShpByteOrder.BigEndian, ShpAtomicType.Integer)]
    private int m_Unused20;
    [ShpType(ShpByteOrder.BigEndian, ShpAtomicType.Integer)]
    private int m_FileLength;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Integer)]
    private int m_Version;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Integer)]
    private int m_ShapeType;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Double)]
    private double m_Xmin;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Double)]
    private double m_Ymin;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Double)]
    private double m_Xmax;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Double)]
    private double m_Ymax;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Double)]
    private double m_Zmin;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Double)]
    private double m_Zmax;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Double)]
    private double m_Mmin;
    [ShpType(ShpByteOrder.LittleEndian, ShpAtomicType.Double)]
    private double m_Mmax;
    #endregion

    #region Properties
    public int FileCode {
      get { return m_FileCode; }
      set { m_FileCode = value; }
    }

    public int Unused4 {
      get { return m_Unused4; }
      set { m_Unused4 = value; }
    }

    public int Unused8 {
      get { return m_Unused8; }
      set { m_Unused8 = value; }
    }

    public int Unused12 {
      get { return m_Unused12; }
      set { m_Unused12 = value; }
    }

    public int Unused16 {
      get { return m_Unused16; }
      set { m_Unused16 = value; }
    }

    public int Unused20 {
      get { return m_Unused20; }
      set { m_Unused20 = value; }
    }

    public int Version {
      get { return m_Version; }
      set { m_Version = value; }
    }

    /// <summary>
    /// Length of Shp file
    /// </summary>
    public int FileLength {
      get { return m_FileLength; }
      set { m_FileLength = value; }
    }

    /// <summary>
    /// Type of Shp file
    /// </summary>
    public ShpShapeType ShapeType {
      get { return (ShpShapeType)m_ShapeType; }
      set { m_ShapeType = (int)value; }
    }

    /// <summary>
    /// Bounding box - Xmin
    /// </summary>
    public double XMin {
      get { return m_Xmin; }
      set { m_Xmin = value; }
    }

    /// <summary>
    /// Bounding box - Ymin
    /// </summary>
    public double YMin {
      get { return m_Ymin; }
      set { m_Ymin = value; }
    }

    /// <summary>
    /// Bounding box - Zmin
    /// </summary>
    public double ZMin {
      get { return m_Zmin; }
      set { m_Zmin = value; }
    }

    /// <summary>
    /// Bounding box - Mmin
    /// </summary>
    public double MMin {
      get { return m_Mmin; }
      set { m_Mmin = value; }
    }

    /// <summary>
    /// Bounding box - Xmax
    /// </summary>
    public double XMax {
      get { return m_Xmax; }
      set { m_Xmax = value; }
    }

    /// <summary>
    /// Bounding box - Ymax
    /// </summary>
    public double YMax {
      get { return m_Ymax; }
      set { m_Ymax = value; }
    }

    /// <summary>
    /// Bounding box - Zmax
    /// </summary>
    public double ZMax {
      get { return m_Zmax; }
      set { m_Zmax = value; }
    }

    /// <summary>
    /// Bounding box - Mmax
    /// </summary>
    public double MMax {
      get { return m_Mmax; }
      set { m_Mmax = value; }
    }
    #endregion

    /// <summary>
    /// Create Shp Header from stream
    /// </summary>
    /// <param name="stream">Stream to Shp file</param>
    public ShpHeader(Stream stream) {
      FieldInfo[] fields = GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
      foreach (FieldInfo fieldInfo in fields) {
        ShpTypeAttribute[] shpInfos = (ShpTypeAttribute[])fieldInfo.GetCustomAttributes(typeof(ShpTypeAttribute), false);
        if (shpInfos != null && shpInfos.Length > 0) {
          if (shpInfos.Length > 1)
            throw new ShpException("ShpRecord declared more than once for one attribute.");
          ShpTypeAttribute shpInfo = shpInfos[0];
          object value = shpInfo.LoadType(stream);
          fieldInfo.SetValue(this, value);
        }
      }
      if (m_FileCode != 9994 ||
        m_Unused4 != 0 ||
        m_Unused8 != 0 ||
        m_Unused12 != 0 ||
        m_Unused16 != 0 ||
        m_Unused20 != 0 ||
        m_Version != 1000 ||
        stream.Length != m_FileLength * 2
        ) throw new ShpException("Shp file corrupted.");
    }
  }
}
